const BigInt32 = BigInt(32);

export function getBigInt64(dataView: DataView, byteOffset: number, littleEndian: boolean | undefined): bigint {
    const littleEndianMask = Number(!!littleEndian);
    const bigEndianMask = Number(!littleEndian);

    return (
        BigInt(
            dataView.getInt32(byteOffset, littleEndian) * bigEndianMask +
            dataView.getInt32(byteOffset + 4, littleEndian) * littleEndianMask
        ) << BigInt32) |
        BigInt(
            dataView.getUint32(byteOffset, littleEndian) * littleEndianMask +
            dataView.getUint32(byteOffset + 4, littleEndian) * bigEndianMask
        );
}

export function getBigUint64(dataView: DataView, byteOffset: number, littleEndian: boolean | undefined): bigint {
    const a = dataView.getUint32(byteOffset, littleEndian);
    const b = dataView.getUint32(byteOffset + 4, littleEndian);

    const littleEndianMask = Number(!!littleEndian);
    const bigEndianMask = Number(!littleEndian);

    // This branch-less optimization is 77x faster than normal ternary operator.
    // and only 3% slower than native implementation
    // https://jsbench.me/p8kyhg1eqv/1
    return (BigInt(a * bigEndianMask + b * littleEndianMask) << BigInt32) |
        BigInt(a * littleEndianMask + b * bigEndianMask);
};

export function setBigInt64(dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) {
    const hi = Number(value >> BigInt32);
    const lo = Number(value & BigInt(0xFFFFFFFF));

    if (littleEndian) {
        dataView.setInt32(byteOffset + 4, hi, littleEndian);
        dataView.setUint32(byteOffset, lo, littleEndian);
    } else {
        dataView.setInt32(byteOffset, hi, littleEndian);
        dataView.setUint32(byteOffset + 4, lo, littleEndian);
    }
}

export function setBigUint64(dataView: DataView, byteOffset: number, value: bigint, littleEndian: boolean | undefined) {
    const hi = Number(value >> BigInt32);
    const lo = Number(value & BigInt(0xFFFFFFFF));

    if (littleEndian) {
        dataView.setUint32(byteOffset + 4, hi, littleEndian);
        dataView.setUint32(byteOffset, lo, littleEndian);
    } else {
        dataView.setUint32(byteOffset, hi, littleEndian);
        dataView.setUint32(byteOffset + 4, lo, littleEndian);
    }
};
